package com.symaster.pdfcompose.frame.component.scrollbar;

import com.symaster.common.swing.util.SwingUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseWheelEvent;

import static com.symaster.pdfcompose.util.TimeUtil.floatTime;

/**
 * @author yinmiao
 * @version 2023/12/4
 */
public class Scrollbar extends JComponent {
    private final Object lockObj = new Object();
    private final Color barColor = new Color(0, 0, 0, 40);
    private float speed = 1000;
    private float amount = 60f;
    private int viewBar = 0; // 0:auto 1:view 2:disable
    private Container contentPane;
    private int contentPaneHeight = 0;
    private boolean wheel = false;
    private int direction = 0; // 1: 上; 2: 下
    private Float startTime;
    private Float startPoint;
    private float dstOffset = 0;

    public Scrollbar() {
        this(null);
    }

    public Scrollbar(Container contentPane) {
        addMouseWheelListener(new MouseAdapter() {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                myMouseWheelMoved(e);
            }
        });
        if (contentPane != null) {
            setContentPane(contentPane);
        }
    }

    public void setSpeed(float speed) {
        this.speed = speed;
    }

    public void setAmount(float amount) {
        this.amount = amount;
    }

    public Container getContentPane() {
        return contentPane;
    }

    public void setContentPane(Container contentPane) {
        this.contentPane = contentPane;
        removeAll();
        add(contentPane);
        contentPane.addContainerListener(new ContainerListener() {
            @Override
            public void componentAdded(ContainerEvent e) {
                repaint();
            }

            @Override
            public void componentRemoved(ContainerEvent e) {
                repaint();
            }
        });
    }

    public void setViewBar(boolean viewBar) {
        this.viewBar = viewBar ? 1 : 2;
    }

    public void setPoint(int point) {
        dstOffset = -point;
    }

    private void myMouseWheelMoved(MouseWheelEvent e) {
        if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
            synchronized (lockObj) {

                int minPoint = getHeight() - contentPaneHeight;
                if (e.getPreciseWheelRotation() > 0 && dstOffset <= minPoint) {
                    return;
                }
                if (e.getPreciseWheelRotation() < 0 && dstOffset >= 0) {
                    return;
                }

                float preciseWheelRotation = Math.min(Math.max(dstOffset - (float) e.getPreciseWheelRotation() * amount, minPoint), 0);

                startTime = floatTime();
                startPoint = (float) contentPane.getBounds().y;
                direction = dstOffset - preciseWheelRotation > 0 ? 2 : 1;
                dstOffset = preciseWheelRotation;
                wheel = true;

                repaint();
            }
        }
    }

    @Override
    public void paint(Graphics g) {
        float offset = getYOffset();

        contentPane.doLayout();
        contentPaneHeight = contentPane.getPreferredSize().height;
        contentPane.setBounds(0, Math.round(offset), getWidth(), contentPaneHeight);

        super.paint(g);

        if (viewBar == 0 || viewBar == 1) {
            if (contentPaneHeight > getHeight() || viewBar == 1) {
                float barHeight = Math.min((float) getHeight() / (float) contentPaneHeight, 1);
                SwingUtil.antialiasingOn((Graphics2D) g);
                g.setColor(barColor);
                g.fillRoundRect(getWidth() - 4, Math.round(Math.abs(offset) * barHeight), 3, Math.round(getHeight() * barHeight), 3, 3);
            }
        }

        if (wheel) {
            repaint();
        }
    }

    private float getYOffset() {
        synchronized (this.lockObj) {
            float offset;
            if (wheel) {
                float v = (floatTime() - startTime) / 1000 * (direction == 1 ? speed : -speed);
                float v1 = v + startPoint;
                if ((direction == 1 && v1 >= dstOffset) || (direction == 2 && v1 <= dstOffset)) {
                    wheel = false;
                    offset = dstOffset;
                } else {
                    offset = v1;
                }
            } else {
                offset = dstOffset;
            }
            return offset;
        }
    }

}
